Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit 3b20e1c466b0190e321c04c73bf6801033300112


Parents : 195c36f
Author : Ivan <ivan@quad4.io>
Signature : Invalid signer <e46112d44649266d71fe2193e00a4710>, author is <ivan@quad4.io>
Date : 2026-07-08T15:46:36-05:00

feat(RNSH): add support for running embedded modules in frozen executables via --meshchatx-run-module flag

Changes

3 files changed, 68 insertions(+), 0 deletions(-)


Diff

diff --git a/meshchatx/meshchat.py b/meshchatx/meshchat.py
index 94f534b1..e9fabe05 100644
--- a/meshchatx/meshchat.py
+++ b/meshchatx/meshchat.py
@@ -20,6 +20,7 @@ import logging
import os
import platform
import re
+import runpy
import secrets
import shutil
import socket
@@ -19917,8 +19918,33 @@ class ReticulumMeshChat:
return None
+def _maybe_run_embedded_module():
+ """Re-enter a bundled Python module from a frozen MeshChatX executable.
+
+ Desktop builds set ``sys.executable`` to MeshChatX itself, so
+ ``python -m …`` cannot be used to start tools like rnsh. Callers pass
+ ``--meshchatx-run-module <dotted.name>`` followed by that module's argv.
+ """
+ marker = "--meshchatx-run-module"
+ if len(sys.argv) < 3 or sys.argv[1] != marker:
+ return False
+ module_name = sys.argv[2]
+ if not module_name or module_name.startswith("-"):
+ print(
+ f"error: {marker} requires a module name",
+ file=sys.stderr,
+ )
+ raise SystemExit(2)
+ sys.argv = [sys.argv[0], *sys.argv[3:]]
+ runpy.run_module(module_name, run_name="__main__", alter_sys=True)
+ return True
+
+
# class to manage config stored in database
def main():
+ if _maybe_run_embedded_module():
+ return
+
# Initialize crash recovery system early to catch startup errors
recovery = CrashRecovery()
recovery.install()

diff --git a/meshchatx/src/backend/rnsh_manager.py b/meshchatx/src/backend/rnsh_manager.py
index d69660ba..cb55b552 100644
--- a/meshchatx/src/backend/rnsh_manager.py
+++ b/meshchatx/src/backend/rnsh_manager.py
@@ -43,6 +43,10 @@ DEFAULT_TERMINAL_COLS = 120
_LISTEN_ADDRESS_RE = re.compile(r"<([0-9a-fA-F]{16,})>")
_RNSH_MODULE = "RNS.Utilities.rnsh.rnsh"
+# cx_Freeze / AppImage bundles set sys.executable to MeshChatX itself, which
+# does not accept Python's ``-m``. meshchat.main() dispatches this flag to
+# runpy.run_module before argparse (see meshchatx/meshchat.py).
+_MESHCHATX_RUN_MODULE_FLAG = "--meshchatx-run-module"
class RNSHSession:
@@ -239,6 +243,10 @@ class RNSHSession:
except (ImportError, ModuleNotFoundError, ValueError, AttributeError):
return False
+ @staticmethod
+ def _is_frozen_executable():
+ return bool(getattr(sys, "frozen", False))
+
@staticmethod
def _resolve_rnsh_launcher():
"""Return argv prefix for launching rnsh.
@@ -247,8 +255,18 @@ class RNSHSession:
console-script wrapper is not executable (common with pip --user
installs) or when Landlock denies executing paths outside allowed
read roots (for example ``~/.local/bin/rnsh``).
+
+ Frozen desktop builds (Windows EXE, AppImage, macOS) set
+ ``sys.executable`` to MeshChatX itself, which rejects ``-m``. Those
+ builds re-enter via ``--meshchatx-run-module`` instead.
"""
if RNSHSession._rnsh_module_available():
+ if RNSHSession._is_frozen_executable():
+ return [
+ sys.executable,
+ _MESHCHATX_RUN_MODULE_FLAG,
+ _RNSH_MODULE,
+ ]
return [sys.executable, "-m", _RNSH_MODULE]
executable = shutil.which("rnsh")
if executable and os.access(executable, os.X_OK):

diff --git a/tests/backend/test_rnsh_api.py b/tests/backend/test_rnsh_api.py
index 4ed1d692..845b71db 100644
--- a/tests/backend/test_rnsh_api.py
+++ b/tests/backend/test_rnsh_api.py
@@ -193,6 +193,7 @@ def test_rnsh_prefers_module_launcher_when_rns_installed(monkeypatch):
from meshchatx.src.backend import rnsh_manager as rnsh_mod
monkeypatch.setattr(rnsh_mod.RNSHSession, "_rnsh_module_available", lambda: True)
+ monkeypatch.setattr(rnsh_mod.RNSHSession, "_is_frozen_executable", lambda: False)
monkeypatch.setattr(
rnsh_mod.shutil, "which", lambda _name: "/home/user/.local/bin/rnsh"
)
@@ -208,6 +209,29 @@ def test_rnsh_prefers_module_launcher_when_rns_installed(monkeypatch):
assert command[-1] == "deadbeef"
+def test_rnsh_frozen_uses_meshchatx_run_module(monkeypatch):
+ from meshchatx.src.backend import rnsh_manager as rnsh_mod
+
+ monkeypatch.setattr(rnsh_mod.RNSHSession, "_rnsh_module_available", lambda: True)
+ monkeypatch.setattr(rnsh_mod.RNSHSession, "_is_frozen_executable", lambda: True)
+ monkeypatch.setattr(rnsh_mod.sys, "executable", r"C:\App\ReticulumMeshChatX.exe")
+
+ manager = MagicMock()
+ session = rnsh_mod.RNSHSession(
+ manager,
+ "s1",
+ {"mode": "connect", "destination": "deadbeef"},
+ )
+ command = session._build_command()
+ assert command[:3] == [
+ r"C:\App\ReticulumMeshChatX.exe",
+ rnsh_mod._MESHCHATX_RUN_MODULE_FLAG,
+ rnsh_mod._RNSH_MODULE,
+ ]
+ assert "-m" not in command
+ assert command[-1] == "deadbeef"
+
+
def test_rnsh_falls_back_to_path_binary_when_module_missing(monkeypatch):
from meshchatx.src.backend import rnsh_manager as rnsh_mod


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────